<?php

namespace Modules\Wechat\Controllers;

use Base\Exception\Controller as Exception;
use Modules\Wechat\Model\Data\OfficialAccount\OAuth as OaOAuthData;
use Modules\Wechat\Model\Data\OfficialAccount\User as OaUserData;
use S\Request;
use S\WeChat;
use S\WeChat\Util\OAuthCookie;
use S\WeChat\Util\Url;

/**
 * OAuth回调控制器的抽象类
 *
 * @package Modules\Wechat\Controllers
 */
abstract class OAuthCallback extends Abstraction
{
    protected function params()
    {
        return [];
    }

    /**
     * 获取code参数
     *
     * 这个参数可以用来换取AccessToken,也能与state参数结合起来用于区分用户是否授权.
     * 如果用户没有授权,则这个参数值为空串或者根本不会传回来.
     *
     * @return string
     */
    protected function getCode()
    {
        return Request::get('code', '');
    }

    /**
     * 获取state参数
     *
     * 这个参数只有授权回调时回传入,可以用来判断是否是授权回调.
     *
     * @param string $default 默认值:没传回来的时候返回
     *
     * @return string
     */
    protected function getState($default = '')
    {
        return Request::get('state', $default);
    }

    /**
     * 用户是否同意授权
     *
     * @return string 返回code的值:换取access_token,为空表示用户没有同意授权.
     */
    protected function isAuthorized()
    {
        return $this->getCode();
    }

    /**
     * 是否是在授权(是否是授权回调)
     *
     * @return string 返回state参数的值:为空时表示不是.
     */
    protected function isAuthorizing()
    {
        return $this->getState('');
    }

    /**
     * 用户授权是否仍然有效
     *
     * @param string $official_account 微信号的公众号
     *
     * @return array 有效时返回 [
     *   'open_id'  => '用户的OpenID',
     *   'union_id' => '用户的UnionID:不一定有'
     * ]
     * @throws Exception
     */
    protected function isAuthorizationStated($official_account)
    {
        if ($open_id = OAuthCookie::getOpenId($official_account)) {
            return (new OaOAuthData)->isInAuthorizationState($official_account, $open_id, true);
        }
        return [];
    }

    /**
     * 用户已授权(授权未过期)时
     *
     * @param string $official_account 公众号的微信号:分享出去的链接中携带的那个.
     * @param string $user_open_id     用户的OpenID
     * @param string $user_union_id    用户的UnionID:这个只有多个公众号绑定到一起之后才有.
     *
     * @return mixed
     */
    abstract protected function onAuthorizationState($official_account, $user_open_id, $user_union_id = '');

    /**
     * 用户手动同意授权或静默授权时执行
     *
     * @param string                   $official_account 连接所属微信号:在URL上传入,建议通过
     *                                                   \S\WeChat\Util\Url::build()方法生成回调URL.
     * @param \Overtrue\Socialite\User $User
     *
     * @return mixed
     */
    abstract protected function onAuthorized($official_account, \Overtrue\Socialite\User $User);

    /**
     * 用户不给授权时执行
     *
     * @param string $official_account 连接所属微信号:在URL上传入,建议通过.
     *                                 \S\WeChat\Util\Url::build()方法生成回调URL
     * @param string $state            回调地址上的state参数.
     *
     * @return mixed
     */
    abstract protected function onUnauthorized($official_account, $state);


    /**
     * 获取令牌
     *
     * @param string $official_account
     * @param string $code
     *
     * @return \Overtrue\Socialite\AccessToken
     */
    protected function getAccessToken($official_account, $code)
    {
        return WeChat::App($official_account)->oauth->getAccessToken($code);
    }

    /**
     * 获取用户
     *
     * @param string                          $official_account
     * @param \Overtrue\Socialite\AccessToken $AccessToken 不传入时自动获取
     *
     * @return \Overtrue\Socialite\User
     */
    protected function getUser($official_account, \Overtrue\Socialite\AccessToken $AccessToken = null)
    {
        return Wechat::app($official_account)->oauth->user($AccessToken);
    }


    /**
     * 重定向到微信授权页面
     *
     * @param string $official_account
     * @param string $redirect_url 微信授权之后回调URL:一般是当前控制器的URL.
     *
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
     */
    protected function getRedirect($official_account, $redirect_url = '')
    {
        return WeChat::app($official_account)
            ->oauth->redirect($redirect_url ?: (APP_HOST_URL . $this->getRequest()->getServer('REQUEST_URI')));
    }


    /**
     * The Action
     *
     * @throws \Base\Exception\Controller
     */
    final protected function action()
    {
        if (!$official_account = Url::getOfficialAccount('')) {
            throw new Exception('参数错误');
        }

        //OAuthCookie::del($official_account);

        //如果用户授权还有效
        if ($open = $this->isAuthorizationStated($official_account)) {
            $this->response = $this->onAuthorizationState($official_account, $open['open_id'], $open['union_id']);
        } else {

            //通过state参数判断是否在授权:没有state就不是在授权(不是微信回调)
            if ($state = $this->isAuthorizing()) {

                //如果用户同意授权
                if ($code = $this->isAuthorized()) {

                    $WeChatApp = WeChat::app($official_account);
                    $User      = $WeChatApp->oauth->user();
                    $oauth     = $User->getOriginal();
                    $user      = $WeChatApp->user->get($oauth['openid']);

                    try {

                        //保存用户OAuth数据
                        (new OaOAuthData)->save(
                            $official_account,
                            $oauth['openid'],
                            $oauth['access_token'],
                            $oauth['expires_in'],
                            $oauth['refresh_token'],
                            $oauth['unionid'],
                            $oauth['scope']
                        );

                        //保存用户信息
                        (new OaUserData)->save(
                            $official_account,
                            $user['openid'],
                            $user['unionid'],
                            $user['subscribe'],
                            $user['subscribe_time'],
                            $user['groupid'],
                            $user['sex'],
                            $user['language'],
                            $user['nickname'],
                            $user['city'],
                            $user['province'],
                            $user['country'],
                            $user['headimgurl'],
                            $user['remark']
                        );

                        //设置Cookie用于检测用户是否已经授权
                        OAuthCookie::set(
                            $official_account,
                            $oauth['openid'],
                            $oauth['unionid'],
                            $oauth['expires_in']
                        );

                    } catch (\Exception $e) {
                        throw new Exception($e->getMessage(), $e->getCode());
                    }

                    //调用上层应用实现的"用户已授权"方法
                    $this->response = $this->onAuthorized($official_account, $User);
                } else {

                    //调用上层应用的"用户未授权"方法,就是用户没给授权
                    $this->response = $this->onUnauthorized($official_account, $state);
                }
            } else {

                //无论如何先清理一下cookie
                OAuthCookie::del($official_account);

                //如果不是微信授权回调,则重定向到微信授权页面
                $this->getRedirect($official_account)->send();
            }
        }

    }

}